//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <mantle.h>
#include <crust.h>
#include <svrobj.h>
#include <ckernel.h>
#include "_sprintf.h"

// Interface functions
//
PInterface CKernel::Probe(
    /* [in] */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(IKernel *)this;
    }
    else if (riid == EIID_IKernel) {
        return (IKernel *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CKernel;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface)&EIID_IKernel;
    }
    return NULL;
}

UInt32 CKernel::AddRef(void)
{
    return 2; // TODO: atomic inc
}

UInt32 CKernel::Release(void)
{
    return 1; // TODO: atomic dec
}

ECode CKernel::Aggregate(
    /* [in] */ AggregateType type,
    /* [in] */ IInterface* pObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CKernel::GetDomain(
    /* [out] */ IInterface** ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CKernel::GetSystemTime(
    /* [out] */ SystemTime *pTime)
{
    if (NULL == pTime) return E_INVALID_ARGUMENT;

    ::DzGetSystemTime(pTime);
    return NOERROR;
}

ECode CKernel::SetSystemTime(
    /* [in] */ SystemTime time)
{
    ::DzSetSystemTime(time);
    return NOERROR;
}

ECode CKernel::GetElapsedTime(
    /* [out] */ Millisecond32 *pTime)
{
    if (NULL == pTime) return E_INVALID_ARGUMENT;

    *pTime = DzTicksToMilliseconds(DzGetElapsedTicks());
    return NOERROR;
}

ECode CKernel::GetIdleTime(
    /* [out] */ Millisecond32 *pTime)
{
    if (NULL == pTime) return E_INVALID_ARGUMENT;

    *pTime = DzTicksToMilliseconds(DzGetIdleTicks());
    return NOERROR;
}

ECode CKernel::CreateProcess(
    /* [out] */ IProcess **ppProcess)
{
    CProcess *pProcess;

    if (NULL == ppProcess) return E_INVALID_ARGUMENT;

    pProcess = new CProcess();
    if (NULL == pProcess) return E_OUT_OF_MEMORY;
    *ppProcess = pProcess;
    pProcess->AddRef();
    return NOERROR;
}

ECode CKernel::GetCurrentProcess(
    /* [out] */ IProcess **ppProcess)
{
    assert(::GetCurrentProcess());

    if (NULL == ppProcess) return E_INVALID_ARGUMENT;

    *ppProcess = ::GetCurrentProcess();
    (*ppProcess)->AddRef();
    return NOERROR;
}

ECode CKernel::GetAllProcesses(
    /* [out] */ IObjectEnumerator **ppProcesses)
{
    IObjectEnumerator *pEnum;

    if (NULL == ppProcesses) return E_INVALID_ARGUMENT;

    pEnum = new CObjectEnumerator(
        &(CProcess::s_processList),
        &(CProcess::s_processListLock),
        &(CProcess::s_nProcessListState),
        NULL);
    if (!pEnum) return E_OUT_OF_MEMORY;

    *ppProcesses = pEnum;
    pEnum->AddRef();
    return NOERROR;
}

EXTERN KMutex g_marshalLock;

ECode CKernel::ExitProcess(
    /* [in] */ Int32 exitCode)
{
    assert(::GetCurrentProcess());

    DLinkNode       *pImportService;
    DLinkNode       *pSharedMem;
    CProcess        *pCurProc;
    Thread          *pCurThd;
    Thread          *pThread;
    uint_t          uPoolThreadState;
    ProcessState    tempProcessState;

    pCurProc = ::GetCurrentProcess();
    pCurThd  = ::GetCurrentThread();

    if (ThreadState_Finished != pCurThd->m_uPoolThreadState) {
        GetSystemTime(&pCurThd->m_finishTime);
        pCurProc->m_nExitCode = exitCode;
    }

    uPoolThreadState = InterlockedExchange(
        (Int32*)&pCurThd->m_uPoolThreadState, ThreadState_Finished);

    if (ProcessState_Finished != pCurProc->m_processState) {
        GetSystemTime(&pCurProc->m_finishTime);
    }

    tempProcessState = (ProcessState)InterlockedExchange(
        (Int32*)&pCurProc->m_processState, ProcessState_Finished);
    if (ThreadState_Finished == uPoolThreadState ||
        ProcessState_Finished == tempProcessState) {
        // Current thread being abort. Do nothing here. Don't return.
        //
        UninterruptibleSleep();
        assert(0);
    }
    assert(ThreadState_Running == uPoolThreadState);

    pCurProc->m_threadLock.Lock();

    // Detach Current Thread
    //
    pCurThd->m_inProcLink.Detach();
    // for detach twice.(In thread release)
    pCurThd->m_inProcLink.Initialize();

    // Abort other threads
    //
    while (!pCurProc->m_threadList.IsEmpty()) {
        pThread = THIS(
            (InProcessLinkNode *)pCurProc->m_threadList.m_pPrev,
            Thread, m_inProcLink);

        if (ThreadState_Finished != pThread->m_uPoolThreadState) {
            GetSystemTime(&pThread->m_finishTime);
        }

        uPoolThreadState = InterlockedExchange(
            (Int32*)&pThread->m_uPoolThreadState, ThreadState_Finished);
        if (ThreadState_Finished == uPoolThreadState) {
            // pThread being abort.
            pCurProc->m_nThreadListState++;
            pCurProc->m_threadLock.Unlock();
            ::DzSleep(0, NULL);
            pCurProc->m_threadLock.Lock();
            continue;
        }
        pThread->m_inProcLink.Detach();
        // for detach twice.(In thread release)
        pThread->m_inProcLink.Initialize();
        pThread->Abort(pCurProc);
        pThread->Release();
    }
    pCurProc->m_nThreadListState++;
    pCurProc->m_threadLock.Unlock();

    pCurProc->m_moduleLock.Lock();
    if (pCurProc->m_pMainModule) {
        pCurProc->m_pMainModule->Release();
        pCurProc->m_pMainModule = NULL;
        pCurProc->m_moduleList.Detach();
        pCurProc->m_moduleList.Initialize();
    }
    pCurProc->m_moduleLock.Unlock();

    // unregister ezcom commom sources
    //
    g_marshalLock.Lock();
    DeleteObjects(pCurProc);
    g_marshalLock.Unlock();

    pCurProc->m_bIsCallDllDetach = FALSE;

    // if some exceptions occured in process, maybe the
    // m_importServiceList is not empty,
    // we release the Object indicated by oid one by one
    // and destroy the list
    //

    //First, we reanrage the imported object list, make kernel objects is
    //at front of the list and other objects at the tail of the list
    //
    pImportService = pCurProc->m_importServiceList.Next();
    DLinkNode *pTailService = &(pCurProc->m_importServiceList);
    while (pImportService != &(pCurProc->m_importServiceList)) {
        DLinkNode *pTempService = pImportService->Next();
        if (((ImportService *)pImportService)->m_pContext
            == CTX_USER_2_KERN) {
            pImportService->Detach();
            pImportService->Initialize();
            pTailService->InsertNext(pImportService);
            pTailService = pImportService;
        }
        pImportService = pTempService;
    }

    //Second, we release imported objects
    //
    pImportService = pCurProc->m_importServiceList.Next();
    while (pImportService != &(pCurProc->m_importServiceList)) {
        pImportService->Detach();
        DetachServer(((ImportService *)pImportService)->m_oid, \
                    ((ImportService *)pImportService)->m_pContext);
        delete(pImportService);
        pImportService = pCurProc->m_importServiceList.Next();
    }

    // detach shared memory which user have forget to do with or
    // when process have exited abnormally
    //
    pSharedMem = pCurProc->m_attachedMemoryList.Next();
    while (pSharedMem != &(pCurProc->m_attachedMemoryList)) {
        pSharedMem->Detach();
        ((AttachedMemory *)pSharedMem)->m_pSharedMem->Detach(pCurProc,
            FALSE, TRUE);
        delete(pSharedMem);
        pSharedMem = pCurProc->m_attachedMemoryList.Next();
    }

    TryToSendReceiveDebugMsg(DEBUGGEE_PROCESS_EXIT, (UInt32)pCurProc);
    pCurProc->FreeProcess();

    pCurThd->m_exitCode = exitCode;
    pCurThd->m_fnKernelEntry = NULL;
    pCurThd->m_bHasUserEntry = FALSE;
    pCurThd->m_pOwner        = NULL;
    pCurThd->Release();

    ExitThread(pCurProc);
    assert(0);
    return NOERROR;
}

ECode CKernel::CreateThread(
    /* [in] */ Address threadMainStartup,
    /* [in] */ Address threadRoutine,
    /* [in] */ Address threadArg,
    /* [in] */ ThreadCreationFlags flags,
    /* [out] */ IThread **ppThread)
{
    uint_t uStackTop;
    Thread *pThread;
    IModule *pUserModule;
    ECode ec;

#if defined(_DEBUG)
    Address moduleBase = 0;
    WStringBuf_<_MAX_PATH> esbModuleName;
    int nLength = 0;
    char szOffset[12];
#endif // _DEBUG

    assert(::GetCurrentProcess());

    if (NULL == threadRoutine
        || (ThreadCreationFlag_Unstarted == flags && NULL == ppThread)
        || (ThreadCreationFlag_Started != flags
            && ThreadCreationFlag_Unstarted != flags)) {
        return E_INVALID_ARGUMENT;
    }

    ec = ::GetCurrentProcess()->GetThread(&pThread);
    if (FAILED(ec)) return ec;

    ec = ::GetCurrentProcess()->GetModuleByAddress(
            (Address)threadRoutine, &pUserModule);
    if (FAILED(ec)) return E_INVALID_ARGUMENT;

#if defined(_DEBUG)
    pUserModule->GetBaseAddress(&moduleBase);
    pUserModule->GetName(&esbModuleName);
#endif // _DEBUG

    pUserModule->Release();

    assert(pThread->m_uUserStackBase);
    uStackTop = pThread->m_uUserStackBase + USER_STACK_SIZE
            - USER_STACK_PRESERVED_SIZE;

    memset((void *)(uStackTop + USER_STACK_PRESERVED_SIZE - TLS_SIZE),
            0, TLS_SIZE);

    // Sub-Thread derives from TL_CALLBACK_SLOT of Parent Thread
    // and call AddRef() when Sub-Therad is running.
    *((PInterface *)(uStackTop + USER_STACK_PRESERVED_SIZE)
        + (TL_CALLBACK_SLOT - TLS_TOTAL_SLOTS)) = *(((PInterface *)
            (::GetCurrentThread()->m_uUserStackBase + USER_STACK_SIZE)) +
                (TL_CALLBACK_SLOT - TLS_TOTAL_SLOTS));

    // Sub-Thread derives from TL_APPLET_SLOT of Parent Thread
    // and call AddRef() when Sub-Therad is running.
    *((PInterface *)(uStackTop + USER_STACK_PRESERVED_SIZE)
        + (TL_APPLET_SLOT - TLS_TOTAL_SLOTS)) = *(((PInterface *)
            (::GetCurrentThread()->m_uUserStackBase + USER_STACK_SIZE)) +
                (TL_APPLET_SLOT - TLS_TOTAL_SLOTS));

    // TODO: this is a temporarily implementation for applet
    *((PInterface *)(uStackTop + USER_STACK_PRESERVED_SIZE)
        + (TL_DOMAIN_SLOT - TLS_TOTAL_SLOTS)) = *(((PInterface *)
            (::GetCurrentThread()->m_uUserStackBase + USER_STACK_SIZE)) +
                (TL_DOMAIN_SLOT - TLS_TOTAL_SLOTS));

    // Sub-Thread derives from TL_APPLET_DIR_SLOT of Parent Thread
    *((WChar **)(uStackTop + USER_STACK_PRESERVED_SIZE)
        + (TL_APPLET_DIR_SLOT - TLS_TOTAL_SLOTS)) = *(((WChar **)
            (::GetCurrentThread()->m_uUserStackBase + USER_STACK_SIZE)) +
                (TL_APPLET_DIR_SLOT - TLS_TOTAL_SLOTS));

#ifndef _mips
    uStackTop = uStackTop - 12;
#else
    uStackTop = uStackTop - 0x1c;
#endif

    *((int*)(uStackTop + 4)) = threadRoutine;
    *((int*)(uStackTop + 8)) = threadArg;

#if defined(_DEBUG)
    nLength = sprintf(szOffset, "+0x%x", threadRoutine - (Int32)moduleBase);
    nLength = MIN(esbModuleName.GetLength(),
                    MAXIMUM_THREAD_NAME_LENGTH - nLength - 1);
    wcsncpy(pThread->m_wszName, (wchar_t *)esbModuleName.GetPayload(),
        nLength);
    AnsiToUnic(pThread->m_wszName + nLength, szOffset);
#endif // _DEBUG

    pThread->SetUserEntry((void *)uStackTop, (void *)threadMainStartup);
    if (ppThread) {
        *ppThread = pThread;
    }

    UInt32 handle[] = {(UInt32)pThread, threadRoutine};
    TryToSendReceiveDebugMsg(DEBUGGEE_THREAD_CREATE, (UInt32)handle);

    if (ThreadCreationFlag_Started == flags) pThread->Start();
    if (NULL == ppThread) pThread->Release();
    return NOERROR;
}

ECode CKernel::GetCurrentThread(
    /* [out] */ IThread **ppThread)
{
    if (NULL == ppThread) return E_INVALID_ARGUMENT;

    *ppThread = ::GetCurrentThread();
    (*ppThread)->AddRef();
    return NOERROR;
}

ECode CKernel::LoadModule(
    /* [in] */ WString wsName,
    /* [in] */ ModuleLoadFlags flags,
    /* [out] */ BufferOf<Address> * pDllMainStartups,
    /* [out] */ IModule **ppMod,
    /* [out] */ Boolean *pNewlyLoaded)
{
    ModuleView  *pModuleView;
    CProcess    *pCurProc;
    ECode ec;

    assert(::GetCurrentProcess());

    if (pDllMainStartups == NULL || NULL == ppMod
        || wsName.IsNull()) {
        return E_INVALID_ARGUMENT;
    }

    pCurProc = ::GetCurrentProcess();

    pDllMainStartups->SetUsed(0);

    // TODO(mips): wchar_t

#if !(defined(_mips) && defined(_GNUC))
    ec = pCurProc->LoadModuleW(
            wsName,
            NULL,
            pDllMainStartups,
            flags, ModuleFileType_DLL, &pModuleView, pNewlyLoaded);
#else
    if (wcslen((wchar_t*)wsName) > 40) {
        kprintf("overflow the alloc, the i = %d\n", i);
        DebugBreak();
    }

    ec = pCurProc->LoadModuleW(
            (wchar_t*)wsName,
            NULL,
            pDllMainStartups,
            flags, ModuleFileType_DLL, &pModuleView, pNewlyLoaded);
#endif

    if (SUCCEEDED(ec)) {
        *ppMod = pModuleView;

        // Don't call (*ppIModule)->AddRef() here,
        // because already AddRef in LoadModuleW
        //
    }

    return ec;
}

ECode CKernel::Sleep(
    /* [in] */ Millisecond32 period,
    /* [out] */ WaitResult *pResult)
{
    if (INFINITE == period) {
        ::DzSleep();
        if (NULL != pResult) *pResult = WaitResult_Interrupted;
    }
    else {
        ::DzSleep(DzMillisecondsToTicks(period), pResult);
    }

    return NOERROR;
}

ECode CKernel::GetDelayFactor(
    /* [out] */ Int32 *ploopCount)
{
    UInt32 uBase = COMMON_BASE - 0x10000;
    UInt32 uSize = PROCESS_TOP - sizeof(Int32) - uBase;

    if (FALL_INTO((UInt32)ploopCount, uBase, uSize)) {
        *ploopCount = (Int32)g_uLoopCountPerMicrosecond;
        return NOERROR;
    }
    else {
        return E_INVALID_ARGUMENT;
    }
}

ECode CKernel::Yield()
{
    ::DzYield();
    return NOERROR;
}

ECode CKernel::VirtualMemoryMap(
    /* [in] */ Address startingAddr,
    /* [in] */ MemorySize size,
    /* [in] */ MemoryMapFlags flags,
    /* [in] */ MemoryProtection protect,
    /* [out] */ Address *pSettledAddr)
{
    assert(::GetCurrentProcess());

    if (NULL == pSettledAddr || PAGE_OFFSET(startingAddr)
        || PAGE_OFFSET(size) ||
        ((flags & MemoryMap_UpGrow) && (flags & MemoryMap_DownGrow))) {
        return E_INVALID_ARGUMENT;
    }

    if (0 >= size) {
        return E_INVALID_ARGUMENT;
    }

    if (size > VIRTUAL_ALLOC_SIZE) {
        return E_NOT_ENOUGH_ADDRESS_SPACE;
    }

    if (flags & MemoryMap_Fixed) {
        if (startingAddr < VIRTUAL_ALLOC_BASE
            || startingAddr >= VIRTUAL_ALLOC_LIMIT) {
            return E_INVALID_ARGUMENT;
        }

        if (startingAddr + size > VIRTUAL_ALLOC_LIMIT) {
            return E_NOT_ENOUGH_ADDRESS_SPACE;
        }
    }

    // we only permit user alloc a private space
    flags = (MemoryMap)((flags | MemoryMap_Private) & (~MemoryMap_Shared));

    //To Guarantee UnIoReamap
    protect &= ~IoRemapFlag_Direct;

    return ::GetCurrentProcess()->m_AS.Alloc( \
        startingAddr, (UInt32)size, (UInt32)flags, protect, (UInt32*)pSettledAddr);
}

ECode CKernel::MemoryQuery(
    /* [in] */ Address address,
    /* [out] */ Address *pBaseAddr,
    /* [out] */ MemorySize *pSize)
{
    assert(::GetCurrentProcess());

    if (NULL == pBaseAddr && NULL == pSize) return E_INVALID_ARGUMENT;

    return ::GetCurrentProcess()->m_AS.Query(
                address, (UInt32*)pBaseAddr, pSize);
}

ECode CKernel::MemoryQueryPhysicalBlock(
    /* [in] */ Address address,
    /* [out] */ Address *pPhysicalBaseAddr,
    /* [out] */ MemorySize *pSize)
{
    return DzMemoryQueryPhysicalBlock(address, pPhysicalBaseAddr, pSize);
}

ECode CKernel::MemoryProtect(
    /* [in] */ Address baseAddr,
    /* [in] */ MemorySize size,
    /* [in] */ MemoryProtection protect)
{
    assert(::GetCurrentProcess());

    if (PAGE_OFFSET(baseAddr) || PAGE_OFFSET(size))
        return E_INVALID_ARGUMENT;

    return ::GetCurrentProcess()->m_AS.SetProtect(
        baseAddr, (UInt32)size, protect);
}

ECode CKernel::MemoryCheck(
    /* [in] */ Address address,
    /* [in] */ MemoryProtection protect,
    /* [out] */ Boolean *pValid)
{
    if (NULL == pValid) return E_INVALID_ARGUMENT;

    assert(::GetCurrentProcess());

    ECode ec = ::GetCurrentProcess()->m_AS.CheckProtect(
                    address, protect, pValid);
    return ec;
}

ECode CKernel::MemoryCommit(
    /* [in] */ Address address)
{
    assert(::GetCurrentProcess());

    if (!FALL_INTO(address, VIRTUAL_ALLOC_BASE, VIRTUAL_ALLOC_SIZE)) {
        return E_INVALID_ARGUMENT;
    }

    return ::GetCurrentProcess()->m_AS.Hit(address);
}

ECode CKernel::IoRemap(
    /* [in] */ Address startingAddr,
    /* [in] */ MemorySize size,
    /* [in] */ Address physicalAddr,
    /* [in] */ MemoryMapFlags flags,
    /* [in] */ Int32 protect,
    /* [out] */ Address *pSettledAddr)
{
    if (0 >= size) {
        return E_INVALID_ARGUMENT;
    }

    if (size > VIRTUAL_ALLOC_SIZE) {
        return E_NOT_ENOUGH_ADDRESS_SPACE;
    }

    // Verify physicalAddr is not physical memory. It must a real IO address.
    if ((0 != physicalAddr + size && physicalAddr >= physicalAddr + size)
        || !IsMemoryMappedIo((physaddr_t)physicalAddr, size)) {
        return E_INVALID_ARGUMENT;
    }

    if (0 == protect) {
        return E_INVALID_ARGUMENT;
    }

    return DzIoRemap(startingAddr, size, physicalAddr,
                flags, protect, pSettledAddr);
}

ECode CKernel::MemoryUnmap(
    /* [in] */ Address baseAddr,
    /* [in] */ MemorySize size)
{
    return DzMemoryUnmap(baseAddr, size);
}

ECode CKernel::CreateMutex(
    /* [out] */ IMutex **ppMutex)
{
    if (NULL == ppMutex) return E_INVALID_ARGUMENT;

    *ppMutex = new CMutex;
    if (NULL == *ppMutex) return E_OUT_OF_MEMORY;
    (*ppMutex)->AddRef();
    return NOERROR;
}

ECode CKernel::CreateNamedMutex(
    /* [in] */ WString name,
    /* [out] */ IMutex **ppMutex,
    /* [out] */ Boolean *pAlreadyExists)
{
    if (name.IsNull() || !ppMutex || !pAlreadyExists) {
        return E_INVALID_ARGUMENT;
    }

    if (name.GetLength(MAXIMUM_SERVICE_NAME_LENGTH + 1) < 0) {
        return E_SERVICE_NAME_TOO_LONG;
    }

    ECode ec = NOERROR;

    g_marshalLock.Lock();

    *pAlreadyExists = FALSE;

    NameHook * pNameHook = NameHook::Find(name);
    if (pNameHook) {
        if (!pNameHook->m_pObject->m_pCProcess) {
            IInterface * pObj = ((CObjectStub *)pNameHook->m_pObject->m_pIStub)->\
                                m_pInterfaces[0].m_pObject;
            *ppMutex = IMutex::Probe(pObj);
            if (*ppMutex) {
                (*ppMutex)->AddRef();
                *pAlreadyExists = TRUE;
            }
            else {
                ec = E_ALREADY_EXIST;
            }
        }
        else {
            ec = E_ALREADY_EXIST;
        }
    }
    else {
        ec = this->CreateMutex(ppMutex);
        if (SUCCEEDED(ec)) {
            ec = CObjectStub::S_CreateObject(name, *ppMutex, NULL);
            if (FAILED(ec)) {
                (*ppMutex)->Release();
            }
        }
    }

    g_marshalLock.Unlock();

    return ec;
}

ECode CKernel::CreateCondition(
    /* [out] */ ICondition **ppCondition)
{
    if (NULL == ppCondition) return E_INVALID_ARGUMENT;

    *ppCondition = new CCondition;
    if (NULL == *ppCondition) return E_OUT_OF_MEMORY;
    (*ppCondition)->AddRef();
    return NOERROR;
}

ECode CKernel::CreateNamedCondition(
    /* [in] */ WString name,
    /* [out] */ ICondition **ppCondition,
    /* [out] */ Boolean *pAlreadyExists)
{
    if (name.IsNull() || !ppCondition || !pAlreadyExists) {
        return E_INVALID_ARGUMENT;
    }

    if (name.GetLength(MAXIMUM_SERVICE_NAME_LENGTH + 1) < 0) {
        return E_SERVICE_NAME_TOO_LONG;
    }

    ECode ec = NOERROR;

    g_marshalLock.Lock();

    *pAlreadyExists = FALSE;

    NameHook * pNameHook = NameHook::Find(name);
    if (pNameHook) {
        if (!pNameHook->m_pObject->m_pCProcess) {
            IInterface * pObj = ((CObjectStub *)pNameHook->m_pObject->m_pIStub)->\
                                m_pInterfaces[0].m_pObject;
            *ppCondition = ICondition::Probe(pObj);
            if (*ppCondition) {
                (*ppCondition)->AddRef();
                *pAlreadyExists = TRUE;
            }
            else {
                ec = E_ALREADY_EXIST;
            }
        }
        else {
            ec = E_ALREADY_EXIST;
        }
    }
    else {
        ec = this->CreateCondition(ppCondition);
        if (SUCCEEDED(ec)) {
            ec = CObjectStub::S_CreateObject(name, *ppCondition, NULL);
            if (FAILED(ec)) {
                (*ppCondition)->Release();
            }
        }
    }

    g_marshalLock.Unlock();

    return ec;
}

ECode CKernel::CreateEvent(
    /* [in] */ Boolean manualReset,
    /* [in] */ EventState initialState,
    /* [out] */ IEvent **ppEvent)
{
    if (NULL == ppEvent) return E_INVALID_ARGUMENT;

    *ppEvent = new CEvent(manualReset, initialState);
    if (NULL == *ppEvent) return E_OUT_OF_MEMORY;
    (*ppEvent)->AddRef();
    return NOERROR;
}

ECode CKernel::CreateNamedEvent(
    /* [in] */ WString name,
    /* [in] */ Boolean manualReset,
    /* [in] */ EventState initialState,
    /* [out] */ IEvent **ppEvent,
    /* [out] */ Boolean *pAlreadyExists)
{
    if (name.IsNull() || !ppEvent || !pAlreadyExists) {
        return E_INVALID_ARGUMENT;
    }

    if (name.GetLength(MAXIMUM_SERVICE_NAME_LENGTH + 1) < 0) {
        return E_SERVICE_NAME_TOO_LONG;
    }

    ECode ec = NOERROR;

    g_marshalLock.Lock();

    *pAlreadyExists = FALSE;

    NameHook * pNameHook = NameHook::Find(name);
    if (pNameHook) {
        if (!pNameHook->m_pObject->m_pCProcess) {
            IInterface * pObj = ((CObjectStub *)pNameHook->m_pObject->m_pIStub)->\
                                m_pInterfaces[0].m_pObject;
            *ppEvent = IEvent::Probe(pObj);
            if (*ppEvent) {
                (*ppEvent)->AddRef();
                *pAlreadyExists = TRUE;
            }
            else {
                ec = E_ALREADY_EXIST;
            }
        }
        else {
            ec = E_ALREADY_EXIST;
        }
    }
    else {
        ec = this->CreateEvent(manualReset, initialState, ppEvent);
        if (SUCCEEDED(ec)) {
            ec = CObjectStub::S_CreateObject(name, *ppEvent, NULL);
            if (FAILED(ec)) {
                (*ppEvent)->Release();
            }
        }
    }

    g_marshalLock.Unlock();

    return ec;
}

ECode CKernel::CreateReaderWriterLock(
    /* [out] */ IReaderWriterLock **ppRWLock)
{
    if (NULL == ppRWLock) return E_INVALID_ARGUMENT;

    *ppRWLock = new CReaderWriterLock;
    if (NULL == *ppRWLock) return E_OUT_OF_MEMORY;
    (*ppRWLock)->AddRef();
    return NOERROR;
}

ECode CKernel::CreateNamedReaderWriterLock(
    /* [in] */ WString name,
    /* [out] */ IReaderWriterLock **ppRWLock,
    /* [out] */ Boolean *pAlreadyExists)
{
    if (name.IsNull() || !ppRWLock || !pAlreadyExists) {
        return E_INVALID_ARGUMENT;
    }

    if (name.GetLength(MAXIMUM_SERVICE_NAME_LENGTH + 1) < 0) {
        return E_SERVICE_NAME_TOO_LONG;
    }

    ECode ec = NOERROR;

    g_marshalLock.Lock();

    *pAlreadyExists = FALSE;

    NameHook * pNameHook = NameHook::Find(name);
    if (pNameHook) {
        if (!pNameHook->m_pObject->m_pCProcess) {
            IInterface * pObj = ((CObjectStub *)pNameHook->m_pObject->m_pIStub)->\
                                m_pInterfaces[0].m_pObject;
            *ppRWLock = IReaderWriterLock::Probe(pObj);
            if (*ppRWLock) {
                (*ppRWLock)->AddRef();
                *pAlreadyExists = TRUE;
            }
            else {
                ec = E_ALREADY_EXIST;
            }
        }
        else {
            ec = E_ALREADY_EXIST;
        }
    }
    else {
        ec = this->CreateReaderWriterLock(ppRWLock);
        if (SUCCEEDED(ec)) {
            ec = CObjectStub::S_CreateObject(name, *ppRWLock, NULL);
            if (FAILED(ec)) {
                (*ppRWLock)->Release();
            }
        }
    }

    g_marshalLock.Unlock();

    return ec;
}

ECode CKernel::CreateSharedMemory(
    /* [in] */ MemorySize size,
    /* [in] */ Boolean commit,
    /* [out] */ ISharedMemory **ppSharedMemory)
{
    ECode ec;
    CSharedMemory *pSharedMemObj;

    if (0 == size || NULL == ppSharedMemory) return E_INVALID_ARGUMENT;

    pSharedMemObj = new CSharedMemory;
    if (NULL == pSharedMemObj) return E_OUT_OF_MEMORY;

    pSharedMemObj->AddRef();

    ec = pSharedMemObj->AllocGlobalAS(AlignPageUp(size), commit);
    if (FAILED(ec)) goto ErrorExit;

    *ppSharedMemory = pSharedMemObj;
    // Already call pSharedMemObj->AddRef();

    return NOERROR;

ErrorExit:
    delete pSharedMemObj;
    return ec;
}

ECode CKernel::CreateNamedSharedMemory(
    /* [in] */ WString name,
    /* [in] */ MemorySize size,
    /* [in] */ Boolean commit,
    /* [out] */ ISharedMemory **ppSharedMemory,
    /* [out] */ Boolean *pAlreadyExists)
{
    if (name.IsNull() || !ppSharedMemory || !pAlreadyExists) {
        return E_INVALID_ARGUMENT;
    }

    if (name.GetLength(MAXIMUM_SERVICE_NAME_LENGTH + 1) < 0) {
        return E_SERVICE_NAME_TOO_LONG;
    }

    ECode ec = NOERROR;

    g_marshalLock.Lock();

    *pAlreadyExists = FALSE;

    NameHook * pNameHook = NameHook::Find(name);
    if (pNameHook) {
        if (!pNameHook->m_pObject->m_pCProcess) {
            IInterface * pObj = ((CObjectStub *)pNameHook->m_pObject->m_pIStub)->\
                                m_pInterfaces[0].m_pObject;
            *ppSharedMemory = ISharedMemory::Probe(pObj);
            if (*ppSharedMemory) {
                (*ppSharedMemory)->AddRef();
                *pAlreadyExists = TRUE;
            }
            else {
                ec = E_ALREADY_EXIST;
            }
        }
        else {
            ec = E_ALREADY_EXIST;
        }
    }
    else {
        ec = this->CreateSharedMemory(size, commit, ppSharedMemory);
        if (SUCCEEDED(ec)) {
            ec = CObjectStub::S_CreateObject(name, *ppSharedMemory, NULL);
            if (FAILED(ec)) {
                (*ppSharedMemory)->Release();
            }
        }
    }

    g_marshalLock.Unlock();

    return ec;
}

ECode CKernel::RequestSystemEvent(
    /* [in] */ SystemEvents interestedEvents,
    /* [out] */ IEvent **ppEvent)
{
    return ::GetCurrentProcess()->RequestSystemEvent(
                interestedEvents, ppEvent);
}

CHostingManager g_hostingManager;

ECode CKernel::GetHostingManager(
    /* [out] */ IHostingManager **ppManager)
{
    if (NULL == ppManager) return E_INVALID_ARGUMENT;
    *ppManager = (IHostingManager *)&g_hostingManager;
    return NOERROR;
}

EXTERN_C IMutex *EnvironMutex();
ECode CKernel::GetEnvironmentMutex(
    /* [out] */ IMutex **ppEnvMutex)
{
    if (NULL == ppEnvMutex) return E_INVALID_ARGUMENT;
    *ppEnvMutex = EnvironMutex();
    (*ppEnvMutex)->AddRef();
    return NOERROR;
}

ECode CKernel::AcquireThreadLocalSlot(
    /* [in] */ ThreadLocalSlotType slotType,
    /* [out] */ Int32 *pSlotIndex)
{
    assert(::GetCurrentProcess());

    return ::GetCurrentProcess()->AcquireThreadLocalSlot(slotType,
        pSlotIndex);
}

ECode CKernel::RelinquishThreadLocalSlot(
    /* [in] */ Int32 slotIndex,
    /* [out] */ BufferOf<Address> * pUsedPtrs)
{
    assert(::GetCurrentProcess());

    return ::GetCurrentProcess()->RelinquishThreadLocalSlot(
            slotIndex, pUsedPtrs);
}

ECode CKernel::CleanUpThreadLocalSlot(
    /* [in] */ Int32 slotIndex,
    /* [out] */ BufferOf<Address> * pUsedPtrs)
{
    assert(::GetCurrentProcess());

    return ::GetCurrentProcess()->CleanUpThreadLocalSlot(
            slotIndex, pUsedPtrs);
}

ECode CKernel::WaitForAnyEvents(
    /* [in] */ const BufferOf<IEvent *> & events,
    /* [out] */ WaitResult *pResult,
    /* [out] */ Int32 *pIndex,
    /* [out] */ EventState *pState)
{
    if (events.IsNullOrEmpty() || NULL == pResult || NULL == pIndex) {
        return E_INVALID_ARGUMENT;
    }

    uint_t uNumberOfEvents = (uint_t)events.GetUsed();
    if (0 == uNumberOfEvents || uNumberOfEvents > MAXIMUM_WAIT_EVENTS) {
        return E_INVALID_ARGUMENT;
    }

    InterfaceID iid;

    uint_t i, j;
    DzEvent *pEvents[MAXIMUM_WAIT_EVENTS];
    for (i = 0; i < uNumberOfEvents; i++) {
        if (NULL == events[(int)i]) return E_INVALID_ARGUMENT;

        iid = *(InterfaceID *) (PInterface) events[(int)i]->Probe(EIID_INTERFACE_INFO);
        if (iid == EIID_IEvent) {
            kprintf("EIID_IEvent %d\n", i);
            pEvents[i] = &((CEvent *)events[(int)i])->m_kevent;
        } else if (iid == EIID_IThread) {
            kprintf("EIID_IThread %d\n", i);
            pEvents[i] = &((Thread *)events[(int)i])->m_joinEvent;
        } else {
            return E_INVALID_ARGUMENT;
        }

        for (j = 0; j < i; j++) {
            if (pEvents[j] == pEvents[i]) return E_INVALID_ARGUMENT;
        }
    }

    DzWaitForAnyEvent(
        uNumberOfEvents, pEvents, pResult, pIndex, pState);
    return NOERROR;
}

ECode CKernel::TryWaitForAnyEvents(
    /* [in] */ const BufferOf<IEvent *> & events,
    /* [in] */ Millisecond32 timeout,
    /* [out] */ WaitResult *pResult,
    /* [out] */ Int32 *pIndex,
    /* [out] */ EventState *pState)
{
    if (events.IsNullOrEmpty() || NULL == pResult || NULL == pIndex) {
        return E_INVALID_ARGUMENT;
    }

    uint_t uNumberOfEvents = (uint_t)events.GetUsed();
    if (0 == uNumberOfEvents || uNumberOfEvents > MAXIMUM_WAIT_EVENTS) {
        return E_INVALID_ARGUMENT;
    }

    InterfaceID iid;
    uint_t i, j;
    DzEvent *pEvents[MAXIMUM_WAIT_EVENTS];
    for (i = 0; i < uNumberOfEvents; i++) {
        if (NULL == events[(int)i]) return E_INVALID_ARGUMENT;

        iid = *(InterfaceID *) (PInterface) events[(int)i]->Probe(EIID_INTERFACE_INFO);
        if (iid == EIID_IEvent) {
            kprintf("EIID_IEvent %d\n", i);
            pEvents[i] = &((CEvent *)events[(int)i])->m_kevent;
        } else if (iid == EIID_IThread) {
            kprintf("EIID_IThread %d\n", i);
            pEvents[i] = &((Thread *)events[(int)i])->m_joinEvent;
        } else {
            return E_INVALID_ARGUMENT;
        }

        for (j = 0; j < i; j++) {
            if (pEvents[j] == pEvents[i]) return E_INVALID_ARGUMENT;
        }
    }

    DzTryWaitForAnyEvent(
        uNumberOfEvents, pEvents, DzMillisecondsToTicks(timeout),
        pResult, pIndex, pState);
    return NOERROR;
}

ECode CKernel::GetGlobalMemorySizes(
    /* [out] */ Int32 *pTotalPhysicalMemorySize,
    /* [out] */ Int32 *pAvailablePhysicalMemorySize)
{
    if (NULL == pTotalPhysicalMemorySize
        && NULL == pAvailablePhysicalMemorySize) {
        return E_INVALID_ARGUMENT;
    }

    if (NULL != pTotalPhysicalMemorySize) {
        *pTotalPhysicalMemorySize = g_uNumberOfActualPages * PAGE_SIZE;
    }

    if (NULL != pAvailablePhysicalMemorySize) {
        *pAvailablePhysicalMemorySize = GetNumberOfFreePages() * PAGE_SIZE;
    }

    return NOERROR;
}

ECode CKernel::FlushInstructionCache(
    /* [in] */ Address base,
    /* [in] */ MemorySize size)
{
    UInt32 uBase = (UInt32)base;
    UInt32 uLimit = uBase + (UInt32)size;

    UInt32 uUserBase = COMMON_BASE - 0x10000;
    UInt32 uUserSize = PROCESS_TOP - uUserBase;

    if ((uBase < uLimit) && FALL_INTO(uBase, uUserBase, uUserSize)
            && FALL_INTO(uLimit - 1, uUserBase, uUserSize)) {
        ::FlushInstructionCache(uBase, uLimit);
        return NOERROR;
    }
    else {
        return E_INVALID_ARGUMENT;
    }
}

// member functions
//

EXTERN ECode DzCreateEventObj(
    /* [in] */ Boolean manualReset,
    /* [in] */ EventState initialState,
    /* [out] */ IEvent **ppEvent)
{
    assert(ppEvent);

    *ppEvent = new CEvent(manualReset, initialState);
    if (NULL == *ppEvent) return E_OUT_OF_MEMORY;
    (*ppEvent)->AddRef();
    return NOERROR;
}

EXTERN void DzNotifyEventObjByIsr(
    /* [in] */ IEvent *pEvent,
    /* [in] */ EventState state)
{
    assert(pEvent);

    ((CEvent *)pEvent)->NotifyByIsr(state);
}

EXTERN void DzWaitForAnyEventObj(
    uint_t uNumberOfEventObjs, IEvent **pEventObjs,
    WaitResult *pResult, Int32 *pIndex, EventState *pState)
{
    assert(0 < uNumberOfEventObjs &&
        uNumberOfEventObjs <= MAXIMUM_WAIT_EVENTS);
    assert(pEventObjs);
    assert(pResult);
    assert(pIndex);

    DzEvent *pEvents[MAXIMUM_WAIT_EVENTS];
    for (uint_t i = 0; i < uNumberOfEventObjs; i++) {
        pEvents[i] = (DzEvent *)(CEvent *)pEventObjs[i];
    }

    DzWaitForAnyEvent(
        uNumberOfEventObjs, pEvents, pResult, pIndex, pState);
}

EXTERN void DzTryWaitForAnyEventObj(
    uint_t uNumberOfEventObjs, IEvent **pEventObjs, Millisecond32 ticks,
    WaitResult *pResult, Int32 *pIndex, EventState *pState)
{
    assert(0 < uNumberOfEventObjs &&
        uNumberOfEventObjs <= MAXIMUM_WAIT_EVENTS);
    assert(pEventObjs);
    assert(pResult);
    assert(pIndex);

    DzEvent *pEvents[MAXIMUM_WAIT_EVENTS];
    for (uint_t i = 0; i < uNumberOfEventObjs; i++) {
        pEvents[i] = (DzEvent *)(CEvent *)pEventObjs[i];
    }

    DzTryWaitForAnyEvent(
        uNumberOfEventObjs, pEvents, ticks, pResult, pIndex, pState);
}
